package controller

import (
	"fmt"
	"strings"

	"gddgame.cc/galaxy/binocle"
	. "gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/attr"
	. "gddgame.cc/galaxy/common/dsl/game/base"
	. "gddgame.cc/galaxy/common/dsl/game/config"
	. "gddgame.cc/galaxy/common/dsl/game/group"
	. "gddgame.cc/galaxy/common/dsl/game/language"
	. "gddgame.cc/galaxy/common/dsl/game/skill"
	. "gddgame.cc/galaxy/common/dsl/game/stat"
	. "gddgame.cc/galaxy/common/dsl/game/sync"
	. "gddgame.cc/galaxy/common/dsl/game/user"
	"gddgame.cc/galaxy/common/namespaces/cooperate"
	"gddgame.cc/galaxy/common/namespaces/cooperate/structure"
	"gddgame.cc/galaxy/core/sdk"
	"gddgame.cc/galaxy/project/breeder/breeder/center"
	"gddgame.cc/galaxy/project/breeder/breeder/cooperated"
	"gddgame.cc/galaxy/project/breeder/breeder/model"
	"gddgame.cc/galaxy/server/composer"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/orm"
	"gddgame.cc/galaxy/utils/serialize"
)

var (
	Center *center.Center

	Env   *Environment
	Agent binocle.App

	MapModel     *orm.Model
	MapNoModel   *orm.Model
	MapNodeModel *orm.Model
	UserMapModel *orm.Model

	TM    = cooperated.NewTypeManager()
	start = false
)

func bindData(builder composer.Builder, cc *center.Center) error {
	var err error

	// config
	if _, err := GetConfigData(Env, model.InitConfig); err != nil {
		return err
	}

	// stat
	if cc.StatData, err = GetStatData(Env); err != nil {
		return err
	}

	// group
	if cc.GroupData, err = GetGroupData(Env); err != nil {
		return err
	}

	// user
	if cc.UserData, err = GetUserData(Env); err != nil {
		return nil
	}

	if cc.BaseData, err = GetBaseData(Env); err != nil {
		return err
	}

	if cc.AttrData, err = GetAttrData(Env); err != nil {
		return err
	}

	if cc.SkillData, err = GetSkillData(Env); err != nil {
		return err
	}

	if cc.LanguageData, err = GetLanguageData(Env); err != nil {
		return err
	}

	if cc.SyncData, err = GetSyncData(Env); err != nil {
		return err
	}

	if cc.ModelData, err = model.GetModelData(Env, cc.LanguageData, cc.SyncData); err != nil {
		return err
	}

	for _, item := range cc.BuildList {
		cc.BindDataType(cooperated.BuildType, item.Id, item.Key)
		for _, level := range item.Levels {
			cc.BindOperation(cooperated.BuildType, item.Id, level.Level, "upgrade", level.Require, level.Consume)
		}
	}

	for _, item := range cc.UnitList {
		cc.BindDataType(cooperated.UnitType, item.Id, item.Key)
		for _, level := range item.Levels {
			cc.BindOperation(cooperated.UnitType, item.Id, level.Level, "upgrade", level.Require, level.Consume)
		}
	}

	for _, item := range cc.ItemList {
		cc.BindDataType(cooperated.ItemType, item.Id, item.Key)
	}

	for _, item := range cc.NodeList {
		cc.BindDataType(cooperated.NodeType, item.Id, item.Key)
	}

	cc.SyncDefineList = map[structure.PublicDataType]map[string][]string{}
	buildDefineList := map[string][]string{}
	for id, build := range cc.BuildMap {
		key := cc.TargetKeyMap[center.BuildType][id]
		buildDefineList[key] = build.SyncKeys
	}
	cc.SyncDefineList[center.BuildType] = buildDefineList
	nodeDefineList := map[string][]string{}
	for id, node := range cc.NodeMap {
		key := cc.TargetKeyMap[center.NodeType][id]
		nodeDefineList[key] = node.SyncKeys
	}
	cc.SyncDefineList[center.NodeType] = nodeDefineList
	itemDefineList := map[string][]string{}
	for id, item := range cc.ItemMap {
		key := cc.TargetKeyMap[center.ItemType][id]
		itemDefineList[key] = item.SyncKeys
	}
	cc.SyncDefineList[center.ItemType] = itemDefineList
	unitDefineList := map[string][]string{}
	for id, unit := range cc.UnitMap {
		key := cc.TargetKeyMap[center.UnitType][id]
		unitDefineList[key] = unit.SyncKeys
	}
	cc.SyncDefineList[center.UnitType] = unitDefineList

	cc.UpdateReceive = UpdateReceive
	cc.DownUnit = DownUnit
	cc.GetUserByAccount = GetUserByAccount
	cc.ValidAndUseUnitProp = ValidAndUseUnitProp
	cc.ValidCreateBuild = ValidCreateBuild
	cc.GetUnitWithUser = GetUnitWithUser

	MapModel, err = Env.GetModel(model.MapName)
	if err != nil {
		return err
	}
	MapNoModel, err = Env.GetModel(model.MapNoName)
	if err != nil {
		return err
	}
	MapNodeModel, err = Env.GetModel(model.MapNodeName)
	if err != nil {
		return err
	}
	UserMapModel, err = Env.GetModel(model.UserMapName)
	if err != nil {
		return err
	}

	return nil
}

func initData(builder composer.Builder, cc *center.Center) {
	cc.BindThresholdEvent(center.UnitUpgradeThresholdEvent, func(mapInstance cooperate.MapProxy, object cooperate.PropsInstance, params []interface{}) {
		bag := serialize.GetBag()
		bag.SetList(params)
		level := bag.ReadInt(0)
		bag.Release()
		userUnitId := utils.ToString(object.GetId())
		accountId := object.ToInt(cooperate.AccountDataType)
		context := Env.Context()
		req := builder.Context("123")
		if accountId > 0 {
			user, err := GetUserByAccount(context, accountId)
			userUnit, err := GetUnitWithUser(context, userUnitId, user)
			if err != nil {
				fmt.Println("upgrade trroop", err)
				return
			}
			if userUnit == nil {
				fmt.Println("部队为空", err)
				return
			}
			if err := UpUnit(context, user, userUnit, object.(*cooperated.Unit), level); err != nil {
				fmt.Println("upgrade trroop", err)
				return
			}
			_ = req.Set("user_unit", userUnit)
		}
		mapInstance.OnQuery(accountId, object.ToInt(cooperated.Identity), cooperated.Upgrade, []interface{}{cooperated.UnitType, userUnitId, level})
		req.Release()
	}, func(mapInstance cooperate.MapProxy, dataType structure.PublicDataType, id interface{}, params []interface{}) cooperate.PropsInstance {
		return Center.GetData(mapInstance.GetMapNoId(), cooperated.UnitType, id)
	})
	// center
	cc.Compute()
}

func InitGame(builder composer.Builder, app binocle.App, client *sdk.AppClient) error {
	builder.Infoln("Init Game")
	Env = NewEnvironment(builder, app.Name(), client, builder.Cache(app.FullName()))
	Env.SetApp(app)
	if err := Env.OpenLink(); err != nil {
		return err
	}
	if err := Env.RegisterModule(model.Provider); err != nil {
		return err
	}
	var c *center.Center
	Env.RegisterInit(func(env *Environment) error {
		c = center.NewCenter(Env)

		if err := bindData(builder, c); err != nil {
			return err
		}
		Center = c
		start = true
		model.DefaultModelData = c.ModelData
		model.DefaultSkillData = c.SkillData
		TM.ChangeCenter(c)
		return nil
	})
	Env.RegisterNamespace(func(env *Environment) error {
		c.LogicWrap = env.CooperateManager.Service()
		initData(builder, c)
		return nil
	})
	return Env.Start()
}

func ReloadMap(node string) {
	if err := UpdateStatus(Env.Context(), true, false); err != nil {
		return
	}
	list, _ := ListMapNo(Env.Context(), true)
	waitStop := len(list)

	for _, m := range list {
		stopMap(&m, node, func() bool {
			waitStop -= 1
			return waitStop == 0
		})
	}
	fmt.Println("Stop map number:", len(list))
}

func stopMap(m *model.MapNo, node string, fn func() bool) {
	info := strings.Split(m.AiNode, "/")
	if len(info) == 2 {
		Env.CooperateManager.ReleaseManager(info[0], info[1], m.Id)
		if err := ReleaseMap(Env.Context(), Agent.Namespace(), m.Id, info[1], info[0]); err != nil {
			fmt.Println(err)
		}
	}
	Env.CooperateManager.StopMap(m.Id, func() {
		if fn() {
			fmt.Println("clear map data")
			if err := Env.CooperateManager.Clear(); err != nil {
				return
			}
			n, err := ClearUnits(Env.Context())
			if err != nil {
				return
			}
			fmt.Println("Clear unit:", n)
			PreLoadMap(node)
		}
	}, node)
}

func PreLoadMap(node string) {
	fmt.Println("Preload map:" + node)
	list, err := ListMapNoWithAuth(Env.Context())
	if err != nil {
		fmt.Println(err)
		return
	}
	if len(list) == 0 {
		return
	}
	ids := make([]int, len(list))
	for i, v := range list {
		ids[i] = v.MapId
	}
	mapList, _ := ListMap(Env.Context(), ids)
	mapMap := make(map[int]*model.Map, len(mapList))
	for _, v := range mapList {
		mapMap[v.Id] = &v
	}
	for _, v := range list {
		Env.CooperateManager.StartMap(v.Id, node)
	}
}
